#
# amarokSyndication - Amarok script for syndicating (atom file format) your music to Google Pages
#
# $Id: amarokSyndication.pl 2008-05-10 19:17:23Z dpino $
#
# (C) 2007,2008 Diego Pino  <pinowsky at gmail com>
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
#

## 
# Functions for handling Atom XML files
#

use lib "$ENV{'HOME'}/.kde/share/apps/amarok/scripts/atomSyndication/";
use lib "$ENV{'HOME'}/.kde/share/apps/amarok/scripts/atomSyndication/lib/";
use lib "$ENV{'HOME'}/.kde/share/apps/amarok/scripts/atomSyndication/modules/";

require "amarokInterface.pl";
require "albumCover.pl";
require "util.pl";

use XML::DOM;
use Data::Dumper;
use Data::UUID;
use URI::Escape;
 
my $XMLDECL = new XML::DOM::XMLDecl;
$XMLDECL->setVersion("1.0");
$XMLDECL->setEncoding("UTF-8");
$XMLDECL->setStandalone("yes");

$ATOM_XMLNS = 'http://www.w3.org/2005/Atom';

    # Read preferences
if (-e "./.conf.rc") {
    $conf_obj = new Config::General("./.conf.rc");

    %config = $conf_obj->getall;
    $username = $config{"username"};
} 

    # Read preferences
if (-e "$ENV{'HOME'}/.kde/share/apps/amarok/scripts/atomSyndication/prefs/preferences.rc") {
    $prefs_obj = new Config::General("$ENV{'HOME'}/.kde/share/apps/amarok/scripts/atomSyndication/prefs/preferences.rc");

    %preferences = $prefs_obj->getall;
    $VERSION = $preferences{"VERSION"};
    $APP_NAME = $preferences{"APP_NAME"};
    $PLUGIN_NAME = $preferences{"PLUGIN_NAME"};
    $ATOM_FILE =  $preferences{"ATOM_FILE"};
    $MAX_ELEMENTS = $preferences{"MAX_ELEMENTS"};
    $debug = $preferences{"debug"};
} 

$URL_ATOM_FILE = "http://$username.googlepages.com/amarok.xml";
$UPLOAD_URL = "http://pages.google.com/upload-file-simple/$username";

my %feed = (
	'ATT:xmlns', $ATOM_XMLNS,
	'title', 'Recent Tracks Played',
	'subtitle', 'Recent Tracks Played',
	'link', {
	   'ATT:href' => $URL_ATOM_FILE,
	   'ATT:rel' => 'self'
	},
	'updated', '',
	'author', {
	   'name' => $author_name,
	   'email' => $author_email,
	},
	'id', ''
);

my %entry = (
	'title', '',
	'link', {
	   'ATT:href' => ''
	},
	'updated', '',
	'author', {
	   'name' => ''
	},
	'summary','',
	'content', {
	   'ATT:type' => '',
	   'ATT:src' => ''
	},
	'id',''
);

my %albumCovers;

##
# Fetches an Atom file from an URL
#
# @return XML DOM or null if it fails
#
sub GetXML
{
   local($url) = @_;
   local($parser, $xmldoc, $xmldecl);

   # Try to parse XML file
   eval {
      $parser = XML::DOM::Parser->new();
      $xmldoc = $parser->parsefile($url);
   };

   # It fails if XML does no exist or was not-well formed
   if ($@) {
      return undef;
   }

   # XML file exist but has no XML declaration
   $xmldecl = $xmldoc->getXMLDecl();
   if ($xmldecl == undef) {
	$xmldoc->setXMLDecl($XMLDECL);
   }

   return $xmldoc;
}

##
# Transforms an associative array into a XML DOM file
#
sub ArrayToXML
{
       local($xmldoc, $tag, %data) = @_;
   local($node);

   if ($xmldoc == undef) {
	die("XMLdoc is undef");
   }
   $node = $xmldoc->createElement($tag);
   while ( ($key, $value) = each(%data) ) {
      if (ref($value) eq "HASH") {
         $child = &ArrayToXML($xmldoc, $key, %{$data{$key}});
         $node->appendChild($child);
  	 next;
      }

      if ($key =~ /^ATT:(.*)/) {
          $node->setAttribute($1,$value); 
	  next;
      }

      $node->appendChild(&AddElement($xmldoc, $key, $value));
   }

   return $node;
}

## 
# Adds a new element into a XML DOM file
#
sub AddElement
{
   my ($xmldoc, $key, $value) = @_;
   local($node);

   $node = $xmldoc->createElement($key);
   $node->appendChild($xmldoc->createTextNode($value));

   return $node;
}

## 
# Creates a new XML DOM file
#
sub CreateXML
{
   local($node);
   my $xmldoc = new XML::DOM::Document;
   my $xmldecl= new XML::DOM::XMLDecl;

   &debug("CreateXML");

   $feed{'updated'} = &GetTime;
   $feed{'id'} = 'urn:uuid:'.Data::UUID->new->create_str;
   $node = &ArrayToXML($xmldoc, 'feed', %feed);
   $xmldoc->setXMLDecl($XMLDECL);
   $xmldoc->appendChild($node);

   # print DEBUG Dumper($xmldoc);
   return $xmldoc;
}

## 
# Reads entry info from amarok, creates a new node and attaches it to the XML DOM file 
#
sub UpdateXML
{
   local($xmldoc) = @_;
   local($albumCover);

   %musicInfo = &GetMusicInfo;
   if ($lastitle ne $musicInfo{'title'}) {
     $entry{'title'} = $musicInfo{'title'};
     $entry{'summary'} = $musicInfo{'album'};
     $entry{'id'} = 'urn:uuid:'.Data::UUID->new->create_str;
     $entry{'link'}{'ATT:href'} = 'http://www.seeqpod.com/search/?q='.uri_escape($musicInfo{'title'}." ".$musicInfo{'artist'});
     $entry{'author'}{'name'} = $musicInfo{'artist'};
     $entry{'updated'} = &GetTime;

     # Fetch album cover
     $key = $musicInfo{'artist'} . " " . $musicInfo{'album'};
     # Check if it has been already downloaded
     if ($albumCovers{$key} ne "") {
        &debug("Retrieveing album cover from cache...for $key\n");
	$albumCover = $albumCovers{$key};	
     } else {
	&debug("Dowloading album cover...for $key\n");
	$albumCover = &fetchUrlAlbumCover($musicInfo{'artist'}, $musicInfo{'album'});
	&debug("Album cover: $albumCover\n");
	if ($albumCover ne "") {
		$albumCovers{$key} = $albumCover;	
	} else {
		&debug("Could not retrieve album cover for $key\n");
	}
     }
     # Insert into entry if it is an image
     if ($albumCover =~ /.*\.(jpg|png|gif)/i) {
	$type = $1;

	$entry{'content'}{'ATT:type'} = "image/$type";
	$entry{'content'}{'ATT:src'} = $albumCover;
     } else {
		# Do not set cover for this album
	undef($entry{'content'});
     }

     # Convert entry to a node
     $node = &ArrayToXML($xmldoc, 'entry', %entry);
     $feed = ($xmldoc->getElementsByTagName('feed'))[0];
     $entry = ($xmldoc->getElementsByTagName('entry'))[0];
 
     # Delete last child
     @entries = $xmldoc->getElementsByTagName('entry');
     $length = scalar(@entries);
     # debug("Length: " .$length."\n");
     if ($length > $MAX_ELEMENTS) {
 	$feed->removeChild($feed->getLastChild());
     }
 
     # First entry in DOM?
     if ($entry == null) {
 	$feed->appendChild($node);
     } else {
	$feed->insertBefore($node, $entry);
     }
 
     &debug("updated");
   }
} 

## 
# Stores a XML DOM file into a file
#
sub SaveXML
{
   local($xmldoc, $url) = @_;

   $updated = ($xmldoc->getElementsByTagName('updated'))[0];
   $time = &GetTime;
   $updated->getFirstChild->setNodeValue($time);
   &debug("Saving data to $url");
   $xmldoc->printToFile($url);
}

1;

